package com.foreknow.spring;

import com.foreknow.service.AdminService;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 依赖注入：如果想要在控制层访问业务层的方法就需要在控制层注入业务层的接口(无需初始化，初始化的工作交给IOC容器来处理)
 * IOC(控制反转)容器：之前需要我们自己创建的对象现在交给IOC容器来处理(不需要new了)
 * 面试题：什么是依赖注入？什么是IOC容器？如何实现自己的IOC容器？
 */
public class ClassPathXmlApplicationContext implements BeanFactory {
    private Map<String,Object> beans = new HashMap<>();
    // 定义一个构造器进行初始化(创建IOC容器)
    public ClassPathXmlApplicationContext() throws Exception {
        // 解析XML文件
        // 1. 创建一个SaxBuilder的对象
        SAXBuilder builder = new SAXBuilder();
        // 2. 加载配置文件
        Document document = builder.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));
        // 3. 通过document对象获取xml文件的根节点
        Element root = document.getRootElement();
        // 4. 获取到子节点<bean>
        List<Element> list = root.getChildren();
        for (int i = 0;i<list.size();i++) {
            // 获取集合中的bean元素
            Element element = list.get(i);
            // 获取到标签中的属性值
            String id = element.getAttributeValue("id");
            String clazz = element.getAttributeValue("class");
            // System.out.println(id+"----"+clazz);
            // 需要用反射将clazz这个字符串转换为Object对象
            Object o = Class.forName(clazz).newInstance(); // 第二次循环时o获取到的就是adminService对象
            // System.out.println(o);
            // 将获取到的id和对象o保存到Map集合中
            beans.put(id,o);
            // 获取<bean>中的子节点<property>
            for (Element propertyElement:(List<Element>)element.getChildren()) {
                String name = propertyElement.getAttributeValue("name");// adminDao
                String bean = propertyElement.getAttributeValue("bean");// u
                // System.out.println(name+"----"+bean);
                // 从Map中获取到AdminService对象
                Object beanObject = beans.get(bean);  // AdminDaoImpl对象----实现了AdminDao接口
                // 通过反射来调用service中的 public void setAdminDao(AdminDao adminDao) 方法对接口进行初始化
                String methodName = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
                // System.out.println(methodName);
                // beanObject.getClass().getInterfaces()[0] 获取到AdminDaoImpl对象所对应的接口作为方法的形式参数
                Method method = o.getClass().getMethod(methodName,beanObject.getClass().getInterfaces()[0]);
                // 调用 setAdminDao(AdminDao adminDao) 方法
                // o:adminService对象    beanObject:AdminDaoImpl对象
                method.invoke(o,beanObject);
            }
        }
    }

    @Override
    public Object getBean(String id) {
        return null;
    }

    public static void main(String[] args) {
//        try {
//            new ClassPathXmlApplicationContext();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        try {
            BeanFactory beanFactory = new ClassPathXmlApplicationContext();
            AdminService adminService = (AdminService) beanFactory.getBean("adminService");
            // 调用AdminService中的方法
            adminService.getQueryInfo(13);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
